/**
 * @author Jialun Bian
 * @version 1.0
 */
package userInterface;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.math.*;
import javax.swing.*;

import assembuler.Assembuler;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
//The Two Class are used for Animation
import java.util.Timer;
import java.util.TimerTask;

public class MainPanel extends JPanel {
	private Point.Double logoMarkPoint;
	private double directionAngle;
	private boolean penIsOnThePaper;
	private List<Shape> objectList;
	private List<Color> objectColorList;
	public List<String> codeBuffer;
	private TimerTask nowTask;
	private static double TURTLE_SIZE=15.0;
	Timer mainClock;
	public boolean logoIsBusy;
    public MainPanel() {
        super(new BorderLayout());
        //JLabel drawPanel = new JLabel("MainPanel");
        //drawPanel.setPreferredSize(new Dimension(800,600));
        //add(drawPanel,BorderLayout.CENTER);
        setPreferredSize(new Dimension(640,480));
        logoMarkPoint=new Point.Double(320,240);
        objectList=new LinkedList<Shape>();          
        objectColorList=new LinkedList<Color>();
        codeBuffer=new LinkedList<String>();
        penIsOnThePaper=true;
        directionAngle=90;
        logoIsBusy=false;
        mainClock=new Timer(true);
        mainClock.scheduleAtFixedRate(new maintainTask(), new Date(), EnvironmentVariables.freshPeriod);
        setBackground(EnvironmentVariables.backgroundColor);
    }
   public void paint(Graphics g) {
    	super.paintComponent(g);
    	Graphics2D g2=(Graphics2D) g;
    	if (penIsOnThePaper) {
    		g2.setColor(EnvironmentVariables.penColor);
    		Point.Double turtlePointA = new Point.Double(logoMarkPoint.getX()+TURTLE_SIZE*Math.cos(directionAngle*Math.PI/180),logoMarkPoint.getY()-TURTLE_SIZE*Math.sin(directionAngle*Math.PI/180));
    		Point.Double turtlePointB = new Point.Double(logoMarkPoint.getX()+TURTLE_SIZE*Math.cos((directionAngle+120)*Math.PI/180),logoMarkPoint.getY()-TURTLE_SIZE*Math.sin((directionAngle+120)*Math.PI/180));
    		Point.Double turtlePointC = new Point.Double(logoMarkPoint.getX()+TURTLE_SIZE*Math.cos((directionAngle+240)*Math.PI/180),logoMarkPoint.getY()-TURTLE_SIZE*Math.sin((directionAngle+240)*Math.PI/180));
    		g2.draw(new Line2D.Double(logoMarkPoint,turtlePointA));
    		g2.draw(new Line2D.Double(turtlePointA,turtlePointB));
    		g2.draw(new Line2D.Double(turtlePointB,turtlePointC));
    		g2.draw(new Line2D.Double(turtlePointA,turtlePointC));
    	} else {
    		g2.setColor(EnvironmentVariables.penColor);
    		Point.Double turtlePointA = new Point.Double(logoMarkPoint.getX()+TURTLE_SIZE*Math.cos(directionAngle*Math.PI/180),logoMarkPoint.getY()-TURTLE_SIZE*Math.sin(directionAngle*Math.PI/180));
    		g2.draw(new Line2D.Double(logoMarkPoint,turtlePointA));
    		g2.draw(new Ellipse2D.Double(logoMarkPoint.getX()-TURTLE_SIZE,logoMarkPoint.getY()-TURTLE_SIZE,TURTLE_SIZE*2,TURTLE_SIZE*2));
    	}
    	for (int i=0;i<objectList.size();++i) {
    		g2.setColor((Color)objectColorList.get(i));
    		g2.draw((Shape)objectList.get(i));
    	}	
    }
   class maintainTask extends TimerTask {
	   public void run() {
		   if (nowTask==null) {
			   EnvironmentVariables.mainPanel.executeNextCode();
		   } else {
			   nowTask.run();
		   }
	   }
   }
   public void executeNextCode() {
	   if (logoIsBusy || (codeBuffer.isEmpty())) {
		   return;
	   } else {
		   String tmp=codeBuffer.get(0);
		   codeBuffer.remove(0);
		   Assembuler.executeOneLineCode(tmp);
	   }
   }
   
    class MoveRepaintTask extends TimerTask {
    	double leftDistance;

		public MoveRepaintTask(double distance) {
			leftDistance=distance;
		}
		public void run() {
			if (leftDistance>=0) {
				if (leftDistance>EnvironmentVariables.moveSpeed) {
					MoveHelper(EnvironmentVariables.moveSpeed);
					repaint();
					leftDistance-=EnvironmentVariables.moveSpeed;
				} else {
					MoveHelper(leftDistance);
					repaint();
					nowTask=null;
					logoIsBusy=false;
				}
			} else {
				if (leftDistance<-EnvironmentVariables.moveSpeed) {
					MoveHelper(-EnvironmentVariables.moveSpeed);
					repaint();
					leftDistance+=EnvironmentVariables.moveSpeed;
				} else {
					MoveHelper(leftDistance);
					repaint();
					nowTask=null;
					logoIsBusy=false;
				}
			}
		}
    }
    class TurnRepaintTask extends TimerTask {
    	double leftAngle;
		public TurnRepaintTask(double angle) {
			leftAngle=angle;
		}
		@Override
		public void run() {
			if (leftAngle>=0) {
				if (leftAngle>EnvironmentVariables.turnSpeed) {
					TurnHelper(EnvironmentVariables.turnSpeed);
					repaint();
					leftAngle-=EnvironmentVariables.turnSpeed;
				} else {
					TurnHelper(leftAngle);
					repaint();
					nowTask=null;
					logoIsBusy=false;
				}
			} else {
				if (leftAngle<-EnvironmentVariables.turnSpeed) {
					TurnHelper(-EnvironmentVariables.turnSpeed);
					repaint();
					leftAngle+=EnvironmentVariables.turnSpeed;
				} else {
					TurnHelper(leftAngle);
					repaint();
					nowTask=null;
					logoIsBusy=false;
				}
			}
		}

    }
    private int MoveHelper(double distance) {
    	Point.Double newLogoMarkPoint=new Point.Double(logoMarkPoint.getX()+distance*Math.cos(directionAngle*Math.PI/180),logoMarkPoint.getY()-distance*Math.sin(directionAngle*Math.PI/180));
    	if (penIsOnThePaper) {
        	Line2D tmp=new Line2D.Double(logoMarkPoint,newLogoMarkPoint);
        	objectList.add(tmp);
        	objectColorList.add(new Color(EnvironmentVariables.penColor.getRed(),EnvironmentVariables.penColor.getGreen(),EnvironmentVariables.penColor.getBlue()));
    	}
    	logoMarkPoint.setLocation(newLogoMarkPoint);
    	repaint();
        System.out.format("Move Forward %f\n",distance);
        return 0;
    }
    public int FD(double distance) {
    	if (EnvironmentVariables.haveAnimation) {
    		logoIsBusy=true;
    		nowTask=new MoveRepaintTask(distance);
    	} else {
    		MoveHelper(distance);
    	}
    	return 0;
    }

    public int BK(double distance) {
    	FD(-distance);
    	repaint();
        System.out.format("Move BackWard %f\n",distance);
        return 0;
    }
    private int TurnHelper(double angle) {
    	directionAngle+=angle;
    	repaint();
        return 0;
    }
    public int RT(double angle) {
    	if (EnvironmentVariables.haveAnimation) {
    		logoIsBusy=true;
    		nowTask=new TurnRepaintTask(-angle);
    	} else {
    		TurnHelper(-angle);
    	}
    	return 0;
    }
    public int LT(double angle) {
    	RT(-angle);
        System.out.format("Left Turn %f\n",angle);
        return 0;
    }

    public int CLS() {
    	objectList.clear();
    	objectColorList.clear();
    	repaint();
        System.out.println("Clean the Canvas\n");
        return 0;
    }

    public int PU() {
    	penIsOnThePaper=false;
    	repaint();
        System.out.println("Put Up the Pen\n");
        return 0;
    }

    public int PD() {
    	penIsOnThePaper=true;
    	repaint();
        System.out.println("Put Down the Pen\n");
        return 0;
    }

    public int setXY(double x,double y) {
    	logoMarkPoint.setLocation(x,y);
    	repaint();
        System.out.format("Set x=%f y=%f\n",x,y);
        return 0;
    }

    public int setPC(int red,int green,int blue) {
    	EnvironmentVariables.penColor=new Color(red,green,blue);
    	repaint();
        System.out.format("Set Pen Color(RGB)=(%d,%d,%d)\n",red,green,blue);
        return 0;
    }
    
    public int setPC(Color pc) {
    	EnvironmentVariables.penColor=pc;
    	repaint();
    	return 0;
    }

    public int setBG(int red,int green,int blue) {
    	EnvironmentVariables.backgroundColor=new Color(red,green,blue);
        setBackground(EnvironmentVariables.backgroundColor);
        repaint();
        System.out.format("Set BackGround Color(RGB)=(%d,%d,%d)\n",red,green,blue);
        return 0;
    }

    public int setBG(Color bg) {
    	EnvironmentVariables.backgroundColor=bg;
    	setBackground(EnvironmentVariables.backgroundColor);
    	repaint();
    	return 0;
    }
    public int stampOVAL(double w,double h) {
    	if (penIsOnThePaper) {
        	Ellipse2D tmp=new Ellipse2D.Double(logoMarkPoint.getX()-w/2,logoMarkPoint.getY()-h/2,w,h);
        	objectList.add(tmp);
        	objectColorList.add(new Color(EnvironmentVariables.penColor.getRed(),EnvironmentVariables.penColor.getGreen(),EnvironmentVariables.penColor.getBlue()));
    	}
    	repaint();
        System.out.format("Draw a OVAL with width=%f height=%f\n",w,h);
        return 0;
    }
    
    public int setDirection(double d) {
    	directionAngle=d;
    	System.out.format("Set Direction to %f \n", d);
    	repaint();
    	return 0;
    }
    
    public int undo() {
    	if (objectList.size()>0) {
    		objectList.remove(objectList.size()-1);
    		objectColorList.remove(objectColorList.size()-1);
    		repaint();
    		System.out.println("Undo!");
    	} else {
    		System.out.println("Can not Undo!");
    	}
    	return 0;
    }
}
